﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

namespace Sinc.ERP.Application;

public class SequenceInternalService : ISingleton
{
    #region 获取下一个序列值
    private static readonly object _locker = new();
    private static readonly Dictionary<string, Sequence> SeqDict = new();
    /// <summary>
    /// 获取下一个序列值
    /// </summary>
    /// <param name="sequenceName"></param>
    /// <returns></returns>
    [UnitOfWork]
    public string GetNextSequenceValue(string sequenceName)
    {
        if (string.IsNullOrWhiteSpace(sequenceName))
            throw new ArgumentNullException(nameof(sequenceName), $"序列名不能为空");

        var cacheKey = $"System.Sequence.{sequenceName}";
        lock (_locker)
        {
            // System.Console.WriteLine($"{Thread.CurrentThread.Name}:进锁");
            if (!SeqDict.TryGetValue(cacheKey, out var model) || model == null || !model.HasNext())
            {
                model = this.GetSequence(sequenceName) ?? throw new ArgumentOutOfRangeException(nameof(sequenceName), $"序列名无效");
                SeqDict[cacheKey] = model;
            }

            var nextValue = model.GetNextValue();
            // System.Console.WriteLine($"{Thread.CurrentThread.Name}:出锁{nextValue}");
            var dateFormat = model.GetDateFormat();
            return $"{model.Code}{DateTime.Now.ToString(dateFormat)}{nextValue.ToString().PadLeft(model.Length, '0')}";
        }
    }
    #endregion

    #region 获取序列数据
    /// <summary>
    /// 获取序列数据
    /// </summary>
    /// <param name="sequenceName"></param>
    /// <returns></returns>
    private Sequence GetSequence(string sequenceName)
    {
        var _sequenceRep = App.GetService<SqlSugarRepository<Sequence>>();
        var model = _sequenceRep.GetFirst(x => x.Name == sequenceName);
        if (model == null)
            return null;

        var currentValue = model.CurrentValue;
        model.MaxValue = Math.Max(model.MaxValue, model.CurrentValue + model.Increment + model.Cache);
        if (model.IsExpired() || !model.HasNext())
        {
            model.CurrentValue = model.MinValue + model.Increment + model.Cache;
            currentValue = model.MinValue;
        }
        else
        {
            model.CurrentValue += model.Increment + model.Cache;
            currentValue += model.Increment;
        }
        model.UpdateTime = DateTime.Now;
        _sequenceRep.AsUpdateable(model).UpdateColumns(nameof(Sequence.CurrentValue), nameof(Sequence.UpdateTime)).ExecuteCommand();

        model.CurrentValue = currentValue;
        return model;
    }
    #endregion
}
